use std::{io, path::PathBuf};

use libp2p::{
    Multiaddr, TransportError, gossipsub::SubscriptionError, identity::DecodingError, multiaddr,
    noise,
};
use thiserror::Error;

use tokio::task::JoinError;

#[derive(Error, Debug)]
pub enum ServerError {
    #[error("Task join error: {0}")]
    TaskJoinError(#[from] JoinError),

    #[error("P2P network error: {0}")]
    P2pNetwork(#[from] P2pNetworkError),

    #[error("Address parse error: {0}")]
    AddrParseError(#[from] std::net::AddrParseError),

    #[error("File store error: {0}")]
    FileStoreError(#[from] FileStoreError),

    #[error("Init base dir error: {0}")]
    InitBaseDirError(PathBuf),

    #[error("Server I/O error: {0}")]
    IOError(#[from] io::Error),
}

#[derive(Error, Debug)]
pub enum P2pNetworkError {
    #[error("Failed to get directory of the keypair file: {0}")]
    FailedToGetKeypairFileDir(PathBuf),
    #[error("P2P network I/O error: {0}")]
    IOError(#[from] io::Error),
    #[error("Keypair decoding error: {0}")]
    KeypairDecodingError(#[from] DecodingError),
    #[error("Libp2p noise error: {0}")]
    Libp2pNoiseError(#[from] noise::Error),
    #[error("Libp2p builder error: {0}")]
    Libp2pBuilderError(String),
    #[error("Parse libp2p multiaddress error: {0}")]
    Libp2pMultiaddrParseError(#[from] multiaddr::Error),
    #[error("Parsing transport error: {0}")]
    Libp2pTransportError(#[from] TransportError<io::Error>),
    #[error("Libp2p gossipsub subscription error: {0}")]
    Libp2pGossipsubSubscriptionError(#[from] SubscriptionError),

    #[error("Unknown protocol: {0}")]
    UnknownProtocol(Multiaddr),
}

#[derive(Error, Debug)]
pub enum FileStoreError {
    #[error("RocksDB error: {0}")]
    RocksDbError(#[from] rocksdb::Error),

    #[error("Published file not found: {0}")]
    PublishedFileNotFound(u64),

    #[error("Column family missing: {0}")]
    ColumnFamilyMissing(String),

    #[error("Cbor error: {0}")]
    CborError(#[from] serde_cbor::Error),

    #[error("File store I/O error: {0}")]
    IOError(#[from] io::Error),

    #[error("Chunk not found: {0}")]
    ChunkNotFound(usize),
}
